<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>JavaScript</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link rel="stylesheet" href="../styles/article.css" />
  <link rel="shortcut icon" href="../images/logo/github.png" />
</head>

<body>
  <div class="page-header">
    <h1 class="logo"><img data-src="../images/logo/js.png" />JavaScript</h1>
    <ul class="nav">
      <li>
        <a>参考资料</a>
        <ul class="dropdown">
          <li><a href="https://wangdoc.com/javascript/index.html">阮一峰</a></li>
          <li><a href="https://w3school.com.cn/js/index.asp">W3school 教程</a></li>
          <li><a href="https://w3school.com.cn/js/index_pro.asp">W3school 高级教程</a></li>
          <li><a href="https://w3school.com.cn/jsref/index.asp">W3school 参考手册</a></li>
        </ul>
      </li>
    </ul>
  </div>
  <div class="page-sidebar">
    <ul class="menu-root" id="menu"></ul>
  </div>
  <div class="page-container">
    <h1>基础</h1>
    <h2>简介</h2>
    <p class="color-gray">什么是 JavaScript？</p>
    <ul>
      <li><b>轻量级的脚本语言：</b>JavaScript 不需要编译，解析器读一行执行一行。也不具备开发应用程序的能力，但能操控其它大型程序，比如浏览器。</li>
      <li><b>嵌入式语言：</b>JavaScript 本身提供的核心语法不多，只能用来做一些数学和逻辑运算。也不提供任何与 I/O（输入/输出）相关的 API，只能嵌入其它大型应用程序环境，调用宿主环境（host）提供的底层 API，比如浏览器环境和服务器环境（即 Node 项目）。</li>
      <li><b>对象模型语言：</b>JavaScript 从语法角度看是一种 “对象模型” 语言。宿主环境通过这个模型，描述自己的功能和操作接口，从而通过 JavaScript 控制这些功能。</li>
    </ul>
    <h2>语法构成</h2>
    <p class="color-gray">JavaScript 的核心语法相当精简，只包括两个部分：</p>
    <ol>
      <li><b>基本的语法构造：</b>比如操作符、控制结构、语句。</li>
      <li><b>标准库：</b>一系列具有各种功能的对象，比如 Array、Date、Math 等。</li>
    </ol>
    <h2>宿主环境</h2>
    <p class="color-gray">JavaScript 的宿主环境提供额外的 API 以便它调用，这种 API 只能在该环境使用，以浏览器为例可以分成三类：</p>
    <ol>
      <li><b>浏览器控制类：</b>操作浏览器。</li>
      <li><b>DOM 类：</b>操作网页的各种元素。</li>
      <li><b>Web 类：</b>实现互联网的各种功能。</li>
    </ol>
    <h1>基本语法</h1>
    <div class="h2">
      <h2>行</h2>
      <span>&</span>
      <h2>语句</h2>
      <span>&</span>
      <h2>表达式</h2>
    </div>
    <h4>定义</h4>
    <div class="row">
      <div class="col-6">
        <ol>
          <li><b>行：</b>JavaScript 的执行单位，程序从上到下逐行执行。一般情况下一行就是一个语句。</li>
          <li><b>语句：</b>为了完成某种任务而进行的操作。一般情况下不需要返回值。</li>
          <li><b>表达式：</b>为了得到返回值的计算式。一定会有返回值，比如赋值语句的等号右边可以放置各种表达式。</li>
        </ol>
      </div>
      <div class="col-6">
        <pre><code>// 这是一行赋值语句
// 等号右边 1 + 3 是一个表达式
var a = 1 + 3;</code></pre>
      </div>
    </div>
    <h4>语法</h4>
    <div class="row">
      <div class="col-6">
        <ol>
          <li>语句以分号结尾。</li>
          <li>表达式不需要分号结尾。一旦添加分号就会视为语句，这样会产生没有意义的语句。</li>
          <li>分号前面没有内容会视为空语句。</li>
          <li>一行内可以写多个语句，但规范写法应该一行一个语句。</li>
        </ol>
      </div>
      <div class="col-6">
        <pre><code>1 + 3   // 表达式
1 + 3;  // 语句
;;;     // 3个空语句
var a = 1 + 3; var b = 'abc'; // 1行内2个语句</code></pre>
      </div>
    </div>
    <h2>注释</h2>
    <div class="row">
      <ul class="col-6">
        <li><b>定义：</b>源码中被忽略的部分，可以对代码进行解释说明。</li>
        <li><b>单行注释：</b>使用 <code>//</code> 开头。</li>
        <li><b>多行注释：</b>放在 <code>/*</code> 和 <code>*/</code> 之间。</li>
      </ul>
      <div class="col-6">
        <pre><code>// 单行注释
/*
 * 多行注释
 * 多行注释
**/</code></pre>
      </div>
    </div>
    <h2>区块</h2>
    <div class="row">
      <ul class="col-6">
        <li><b>定义：</b>使用大括号，将多个相关的语句组合到一起，成为区块。</li>
        <li>区块对于 <code>var</code> 命令不构成单独的作用域，与不使用区块的情况没有任何区别。</li>
        <li>区块一般不会单独使用，而是用来构成其它更复杂的语法结构，比如 <code>if</code> <code>for</code> 等。</li>
      </ul>
      <div class="col-6">
        <pre><code>{
    var a = 1;  // 代码在区块内部
}
console.log(a); // 1 变量在区块外依然有效</code></pre>
      </div>
    </div>
    <h2>变量</h2>
    <h4>定义</h4>
    <ul>
      <li>变量是存储信息的容器。变量是对“值”的具名引用。变量就是为“值”起名，然后引用这个名字，就等同于引用这个值。</li>
    </ul>
    <h4>语法</h4>
    <div class="row">
      <ol class="col-6">
        <li>使用关键字 <code>var</code> 声明变量，以分号结尾。</li>
        <li>var 可以省略，但规范写法禁止省略。</li>
        <li>一个 <code>var</code> 命令可以声明多个变量，以逗号分隔。</li>
        <li>重复声明一个已经存在的变量，没有赋值时是无效的，有赋值时会覆盖前面的值。</li>
        <li>变量是弱类型的，没有限制，声明时可以初始化为任意值，也可以随时更改。</li>
        <li>声明过的变量没有赋值，该变量的值是 <code>undefined</code>。</li>
        <li>未声明的变量直接使用，系统会报错 <code>not defind</code>。</li>
      </ol>
      <div class="col-6">
        <pre><code>var a = 1;      // 等同于 a = 1
var b, c, d;    // 多个变量
var e = 1;
var e;
console.log(e); // 1 重复声明没有赋值 无效
var e = 2;
console.log(e); // 2 重复声明有赋值 覆盖前面的值
var f = 1;      // 声明类型 number
    f = '1';    // 更改类型 string
var g;          // undefined
console.log(h); // h is not defined</code></pre>
      </div>
    </div>
    <h4>变量提升</h4>
    <div class="row">
      <ul class="col-6">
        <li><b>定义：</b>JavaScript 引擎的工作方式是，先解析代码，获取所有已经声明的变量，再逐行运行。这样所有变量的声明语句，都会被提升到代码的头部，这叫做变量提升。</li>
        <li><b>示例：</b>书写过程先使用变量，再声明赋值，这是是错误的做法，但实际并不会报错。因为存在变零提升，运行时会先声明变量，再使用，再赋值。所以返回值为 undefined 而不是 not defined。</li>
      </ul>
      <div class="col-6">
        <pre><code>// 书写过程
console.log(a); // undefined 而不是 not defined
var a = 1;
// 运行过程
var a;
console.log(a);
a = 1;</code></pre>
      </div>
    </div>
    <h2>标识符</h2>
    <div class="row">
      <div class="col-6">
        <h4>定义</h4>
        <ul>
          <li>用来识别各种值的合法名称。比如变量名和函数名。</li>
        </ul>
        <h4>命名规则</h4>
        <ul>
          <li>第一个字符，可以是任意 Unicode 字母、<code>$</code>、<code>_</code>。</li>
          <li>第二个字符开始，还可以使用数字。</li>
          <li>标识符区分大小写。</li>
          <li>中文是合法标识符，可以用作变量名称，但是一般不会使用。</li>
          <li>JavaScript 的关键字和保留字，不能用作标识符。</li>
        </ul>
      </div>
      <div class="col-6">
        <pre><code>// 合法标识符
abc
$abc
_abc
ABC0
var 变量 = 1;
// 非法标识符
123a    // 第一个字符不能是数字
*a      // 第一个字符不能是 $ _ 以外的符号
a+b-    // 不能包含 $ _ 以外的符号</code></pre>
      </div>
    </div>
    <h1>语句</h1>
    <h2>条件语句</h2>
    <h3>if 结构</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>if</code> 后跟条件表达式，写在圆括号内，表示对表达式求值，根据求值结果执行不同的语句。</li>
        <li>求值结果的布尔值为 <code>true</code> 表示满足条件，执行紧跟在后面的语句，<code>false</code> 则跳过。</li>
        <li>多个语句必须写在大括号里表示区块，一个语句可以省略大括号。</li>
      </ul>
      <div class="col-6">
        <pre><code>if (a === 1) {
    a = a + 1;          // 满足条件时执行
}
if (a === 1) a = a + 1; // 省略大括号</code></pre>
      </div>
    </div>
    <h3>if...else 结构</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>if</code> 语句后跟 <code>else</code> 代码块，表示不满足条件时执行的代码。</li>
        <li><code>else</code> 代码块总是匹配最近的 <code>if</code> 语句。</li>
        <li>多个 <code>if...else</code> 语句连在一起使用，可以多次判断同一个变量。</li>
      </ul>
      <div class="col-6">
        <pre><code>if (m === 3) {
    // 满足条件时执行
} else {
    // 不满足条件时执行
}
if (m === 0) {
    // ...
} else if (m === 1) {
    // ...
} else if (m === 2) {
    // ...
} else {
    // ...
}</code></pre>
      </div>
    </div>
    <h3>switch 结构</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>switch</code> 和 <code>case</code> 后跟变量、表达式等。</li>
        <li><code>switch</code> 后的值如果与 <code>case</code> 后的值相等，会执行对应的 <code>case</code>，都不相等会执行 <code>default</code>。</li>
        <li><code>case</code> 内部需要添加 <code>break</code> 跳出 <code>switch</code> 结构。</li>
        <li><code>switch</code> 相当于多个 <code>if...else</code> 的简便形式。</li>
      </ul>
      <div class="col-6">
        <pre><code>switch (fruit) {
    case 'banana':
        // fruit === 'banana' 时执行
        break;
    case 'apple':
        // ...
        break;
    default:
        // ...
}</code></pre>
      </div>
    </div>
    <h3>三元运算符</h3>
    <div class="row">
      <ul class="col-6">
        <li>三元运算符是 <code>if...else</code> 的简写形式。</li>
        <li>三元运算符是表达式，有返回值。</li>
        <li><code>if...else</code> 是语句，没有返回值。</li>
      </ul>
      <div class="col-6">
        <pre><code>// 三元运算符
var even = (n === 0) ? true : false;
// 等同于
var even;
if (n === 0) {
    even = true;
} else {
    even = false;
}</code></pre>
      </div>
    </div>
    <h2>循环语句</h2>
    <h3>for 循环</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>for</code> 语句写法与 <code>if</code> 语句类似。</li>
        <li>
          <code>for</code> 后跟三个表达式，写在圆括号里。
          <ol>
            <li><b>初始化表达式：</b>确定循环变量的初始值，只在循环开始时执行一次。</li>
            <li><b>条件表达式：</b>每轮循环开始时，都要执行这个条件表达式，只有值为真，才继续进行循环。</li>
            <li><b>递增表达式：</b>每轮循环的最后一个操作，通常用来递增循环变量。</li>
          </ol>
        </li>
      </ul>
      <div class="col-6">
        <pre><code>for (var i = 0; i < 3; i++) {
    console.log(i); // i < 3 时执行 循环3次输出 0 1 2 最后 i === 3
}
for (var i = 0; i < 3; i++) console.log(i); // 省略大括号
</code></pre>
      </div>
    </div>
    <h3>while 循环</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>while</code> 语句写法与 <code>if</code> 语句相同。</li>
        <li><code>for</code> 循环都可以改写成 <code>while</code> 循环。</li>
      </ul>
      <div class="col-6">
        <pre><code>var i = 0;
while (i < 3) {
    console.log(i); // i < 3 时执行 循环3次输出 0 1 2 最后 i === 3
    i++;
}
while (i < 3) i++; // 省略大括号</code></pre>
      </div>
    </div>
    <h3>do...while 循环</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>do...while</code> 循环与 <code>while</code> 循环类似。</li>
        <li><code>do...while</code> 先运行一次循环语句，再判断循环条件。不管条件是否为真都至少运行一次。</li>
        <li><code>while</code> 后的分号不能省略。</li>
      </ul>
      <div class="col-6">
        <pre><code>var i = 0;
do {
    console.log(i); // i < 3 时执行 先执行1次再循环2次输出 0 1 2 最后 i === 3
    i++;
} while (i < 3);
do
    i++; // 省略大括号
while (i < 3);</code></pre>
      </div>
    </div>
    <h3>break 和 continue</h3>
    <div class="row">
      <ul class="col-6">
        <li><code>break</code> 跳出代码块或循环。</li>
        <li><code>continue</code> 立即终止本轮循环，返回循环结构的头部，开始下一轮循环。</li>
      </ul>
      <div class="col-6">
        <pre><code class="javascript">for (var i = 0; i < 5; i++) {
    console.log(i);     // 输出 0 1 2 3
    if (i === 3) break; // i 为 3 时跳出循环
}</code></pre>
        <pre><code class="javascript">for (var i = 0; i < 5; i++) {
    if (i % 2 === 0) continue;  // i 为偶数时跳到下一轮循环
    console.log(i);             // i 为奇数时输出 1 3
}</code></pre>
      </div>
    </div>
    <h3>标签（label）</h3>
    <div class="row">
      <ul class="col-6">
        <li>语句的前面可以添加标签，相当于定位符，用于跳转到程序的任意位置。</li>
        <li>标签可以是任意的标识符，但不能是保留字，语句部分可以是任意语句。</li>
        <li>多重循环中，<code>break</code> 和 <code>continue</code> 只针对内层循环，使用标签可以跳出特定的循环。</li>
      </ul>
      <div class="col-6">
        <pre><code>// 跳出区块 以下输出结果为 1 2
foo: {
    console.log(1);
    break foo;
    console.log('本行不会输出');
}
console.log(2);
// 跳出多重循环
egBreak:
    for (var i = 0; i < 3; i++) {
        for (var j = 0; j < 3; j++) {
            if (i === 1 && j === 1) break egBreak;  // i 为 1 且 j 为 1 时跳出
            console.log(i + '-' + j);               // 输出 0-0 0-1 0-2 1-0
        }
    }
// 跳出多重循环
egContinue:
    for (var i = 0; i < 3; i++) {
        for (var j = 0; j < 3; j++) {
            if (i === 1 && j === 1) continue egContinue;    // i 为 1 且 j 为 1 时跳到下一轮
            console.log(i + '-' + j);                       // 输出 0-0 0-1 0-2 1-0 2-0 2-1 2-2
        }
    }</code></pre>
      </div>
    </div>
    <h1>运算符</h1>
    <h2>运算顺序</h2>
    <div class="row">
      <ul class="col-6">
        <li><b>优先级：</b>优先级高的运算符先执行。</li>
        <li><b>圆括号：</b><code>()</code> 可以提高运算的优先级。它不是运算符，而是一种语法结构。</li>
        <li><b>左结合：</b>优先级相同的运算符，大多数情况，计算顺序总是从左到右。</li>
        <li><b>右结合：</b>赋值运算符 <code>=</code>、指数运算符 <code>**</code>、三元条件运算符 <code>?:</code> 从右边开始计算。</li>
        <li><b>特殊值：</b><code>NaN</code> 与任何值比较，都返回 <code>false</code>，包括它自己。</li>
      </ul>
      <div class="col-6">
        <pre><code class="javascript">1 + 2 * 3;     // 7 先乘后加
(1 + 2) * 3;   // 9 先加后乘
1 > NaN;       // false
NaN == NaN;    // false</code></pre>
      </div>
    </div>
    <h2>算术运算符</h2>
    <p>算术运算符有 10 个，用于数学运算。</p>
    <table class="table-border text-nowrap">
      <thead>
        <tr>
          <th>类型</th>
          <th style="min-width: 115px;">格式</th>
          <th>说明</th>
          <th>示例</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>
            <h3>加法</h3>
          </th>
          <td><code>x + y</code></td>
          <td>
            <ul>
              <li>加法运算符从左到右依次运算。</li>
              <li>加法运算符根据运算子的不同，执行不同的语法行为，比如相加或连接，这叫做<b>重载</b>。</li>
              <li>布尔值转换成数值相加。</li>
              <li>字符串最近的加法运算符变成连接符。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">1 + 1;         // 2 数值相加
1 + true;      // 2 布尔值相加
'1' + '2';     // '12' 字符串相加
'1' + true;    // '1true'
'1' + 2 + 3;   // '123'
1 + 2 + '3';   // '33'</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>减法</h3>
          </th>
          <td><code>x - y</code></td>
          <td rowspan="3">
            <ul>
              <li>不会发生重载。</li>
              <li>运算子先转为数值，再进行相应的数学运算。</li>
            </ul>
          </td>
          <td rowspan="3">
            <pre><code class="javascript">1 - '2'; // -1
1 * '2'; // 2
1 / '2'; // 0.5</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>乘法</h3>
          </th>
          <td><code>x * y</code></td>
        </tr>
        <tr>
          <th>
            <h3>除法</h3>
          </th>
          <td><code>x / y</code></td>
        </tr>
        <tr>
          <th>
            <h3>指数</h3>
          </th>
          <td><code>x ** y</code></td>
          <td>
            <ul>
              <li>前一个运算子是底数，后一个运算子是指数。</li>
              <li><b>右结合：</b>多个指数运算符从右边开始计算。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">2 ** 4;         // 16
2 ** 2 ** 3;    // 256 等同于 2 ** (2 ** 3)</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>余数</h3>
          </th>
          <td><code>x % y</code></td>
          <td>
            <ul>
              <li>运算结果的正负号由第一个运算子决定。</li>
              <li>浮点数的运算无法得到完全准确的结果。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">-5 % 2;    // -1
5 % -2;    // 1
5.1 % 2.1; // 0.8999999999999995</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>自增</h3>
          </th>
          <td><code>x++</code> 或 <code>++x</code></td>
          <td rowspan="2">
            <ul>
              <li>一元运算符，运算子先转为数值，再加1或减1。</li>
              <li>运算符在变量后会先返回值再加减，变量前会先加减再返回值。</li>
              <li><b>运算的副作用：</b>运算后原始变量的值会改变。</li>
            </ul>
          </td>
          <td rowspan="2">
            <pre><code class="javascript">var x = 1;
x++;   // 1
x;     // 2
var y = 1;
++y;   // 2
y;     // 2</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>自减</h3>
          </th>
          <td><code>x--</code> 或 <code>--x</code></td>
        </tr>
        <tr>
          <th>
            <h3>数值</h3>
          </th>
          <td><code>+x</code></td>
          <td rowspan="2">
            <ul>
              <li>数值运算符可以将任何值转为数值，同 <code>Number()</code>。</li>
              <li>运算后返回一个新值，不改变原始变量的值。</li>
            </ul>
          </td>
          <td rowspan="2">
            <pre><code class="javascript">-true; // -1
+true; // 1
+[];   // 0
+{};   // NaN
</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>负数值</h3>
          </th>
          <td><code>-x</code></td>
        </tr>
      </tbody>
    </table>
    <h2>比较运算符</h2>
    <p>比较运算符有 4 个，与相等运算符一样，用于比较两个值的大小，然后返回一个布尔值，表示是否满足指定的条件。它可以比较各种类型的值，不仅仅是数值。</p>
    <table class="table-border text-nowrap">
      <thead>
        <tr>
          <th>类型</th>
          <th>符号</th>
          <th>说明</th>
          <th>示例</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>
            <h3>大于</h3>
          </th>
          <td><code>></code></td>
          <td rowspan="4">
            <ol>
              <li>字符串：按照字典顺序，比较 Unicode 码点大小。</li>
              <li>非字符串的原始类型：先调用 <code>Number()</code> 转成数值，再进行比较。</li>
              <li>非字符串的对象类型：先调用 <code>valueOf()</code> 获取原始值，再进行比较。<br />如果返回值还是对象，再调用 <code>toString()</code> 转成字符串，再进行比较。</li>
            </ol>
          </td>
          <td rowspan="4">
            <pre><code class="javascript">// 1.字符串
'cat' > 'Cat'; // true  c-99 > C-67
'大' > '小';   // false 大-22823 < 小23567
// 2.原始类型
5 > '4';  // true 等同于 5 > Number('4')
2 > true; // true 等同于 2 > Number(true)
// 3.对象
var x = [2]; // x.valueOf() == [2]; x.toString() == '2';
x > '11';    // true  等同于 '2' > '11'
x.valueOf = function() { return '1' }; // x.valueOf() == '1';
x > '11';    // false 等同于 '1' > '11'</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>小于</h3>
          </th>
          <td><code><</code></td>
        </tr>
        <tr>
          <th>
            <h3>小于或等于</h3>
          </th>
          <td><code><=</code></td>
        </tr>
        <tr>
          <th>
            <h3>大于或等于</h3>
          </th>
          <td><code>>=</code></td>
        </tr>
      </tbody>
    </table>
    <h2>相等运算符</h2>
    <p>相等运算符有 4 个，与比较运算符一样，用于比较两个值的大小，然后返回一个布尔值，表示是否满足指定的条件。它可以比较各种类型的值，不仅仅是数值。</p>
    <table class="table-border text-nowrap">
      <thead>
        <tr>
          <th>类型</th>
          <th>符号</th>
          <th>说明</th>
          <th>示例</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>
            <h3>相等</h3>
          </th>
          <td><code>==</code></td>
          <td>
            <ol>
              <li>相同类型：与严格相等运算符完全一样。</li>
              <li>不同的原始类型：先转成数值，再进行比较。</li>
              <li>对象与数值：对象转为数值。</li>
              <li>对象与字符串：对象转为字符串。</li>
              <li>对象与布尔值：对象和布尔值都转成数值。</li>
              <li><code>undefined</code> 和 <code>null</code> 相互比较为 <code>true</code>，与其它值比较为 <code>false</code>。</li>
              <li>相等运算符隐藏的类型转换，很容易出错，应该只使用严格相等运算符。</li>
            </ol>
          </td>
          <td>
            <pre><code class="javascript">// 2.不同的原始类型
true == 1;      // true  等同于 Number(true) === 1
true == 'true'; // false 等同于 Number(true) === Number('true') 即 1 === NaN
// 3.对象与数值
[1] == 1;       // true  等同于 Number([1]) == 1
// 4.对象与字符串
[1] == '1';     // true  等同于 String([1]) == '1'
// 5.对象与布尔值
[1] == true;    // true  等同于 Number([1]) == Number(true)
// 6.undefined 和 null
0 == null;      // false
0 == undefined; // false
undefined == null; // true
</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>严格相等</h3>
          </th>
          <td><code>===</code></td>
          <td>
            <ol>
              <li>不同类型：直接返回 <code>false</code>。</li>
              <li>相同的原始类型：值相同就返回 <code>true</code>，否则返回 <code>false</code>。</li>
              <li>相同的对象类型：比较是否引用同一个内存地址，而不是值。</li>
              <li>不同的变量引用同一个对象时，两个变量相等。</li>
              <li><code>undefined</code> 和 <code>null</code> 与自身严格相等。</li>
            </ol>
          </td>
          <td>
            <pre><code class="javascript">// 1.不同类型
1 === "1";       // false
true === "true"; // false
// 2.相同的原始类型
1 === 0x1;       // true 十进制与十六进制值相同
// 3.相同的对象
[] === [];       // false
{} === {};       // false
function() {} === function() {}; // false
// 4.变量引用同一个对象
var v1 = {};
var v2 = v1;
v1 === v2;       // true
// 5.undefined 和 null
undefined === undefined; // true
null === null;   // true</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>不相等</h3>
          </th>
          <td><code>!=</code></td>
          <td rowspan="2">返回相等/严格相等运算符运算结果的相反值。</td>
          <td rowspan="2">
            <pre><code class="javascript">1 != '1';  // false 等同于 !(1 == '1')
1 !== '1'; // true  等同于 !(1 === '1')</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>严格不相等</h3>
          </th>
          <td><code>!==</code></td>
        </tr>
      </tbody>
    </table>
    <h2>布尔运算符</h2>
    <p>包括 3 个逻辑运算符和 1 个条件运算符，用于将表达式转为布尔值。</p>
    <table class="table-border text-nowrap">
      <thead>
        <tr>
          <th>类型</th>
          <th>符号</th>
          <th>说明</th>
          <th>示例</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>
            <h3>且 and</h3>
          </th>
          <td><code>&&</code></td>
          <td>
            <ul>
              <li>用于多个表达式的求值，从左到右依次运算。</li>
              <li>如果表达式的布尔值为 <code>false</code> ，返回表达式的值且不再对后面的表达式求值。</li>
              <li>如果表达式的布尔值为 <code>true</code>，继续求值。</li>
              <li>如果所有表达式的布尔值都为 <code>true</code>，返回最后表达式的值。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">0 && 1;        // 0 第一个是false
1 && 0;        // 0 第二个是false
1 && 2;        // 2 都是true
1 && 2 && 3;   // 3 都是true</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>或 or</h3>
          </th>
          <td><code>||</code></td>
          <td>
            <ul>
              <li>用于多个表达式的求值，从左到右依次运算。</li>
              <li>如果表达式的布尔值为 <code>true</code>，返回表达式的值且不再对后面的表达式求值。</li>
              <li>如果表达式的布尔值为 <code>false</code> ，继续求值。</li>
              <li>如果所有表达式的布尔值都为 <code>false</code>，返回最后表达式的值。</li>
              <li>或运算符常用于为一个变量设置默认值。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">1 || 0;            // 1 第一个是true
0 || 1;            // 1 第二个是true
0 || '';           // '' 都是false
0 || '' || null;   // null 都是false
x = res.x || 1;    // 先取返回值，没有时设置默认值1</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>否 not</h3>
          </th>
          <td><code>!</code></td>
          <td>
            <ul>
              <li>用于将布尔值转为相反值，非布尔值会先转为布尔值。</li>
              <li>以下六个值取反后为 <code>true</code>，其它所有值都为 <code>false</code>。</li>
              <ol>
                <li><code>undefined</code></li>
                <li><code>null</code></li>
                <li><code>false</code></li>
                <li><code>0</code></li>
                <li><code>NaN</code></li>
                <li>空字符串（<code>''</code>）</li>
              </ol>
              <li>两次取反可以转为对应的布尔值，与 <code>Boolean()</code> 相同。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">!undefined  // true
!null       // true
!false      // true
!0          // true
!NaN        // true
!""         // true
!true       // false
!1          // false
!'1'        // false
![]         // false
!!x         // 等同于 Boolean(x)</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>三元运算符</h3>
          </th>
          <td><code>?:</code></td>
          <td>
            <ul>
              <li><b>语法：</b>(条件) ? 表达式1 : 表达式2</li>
              <li><b>右结合：</b>多个条件时从右边开始计算。</li>
              <li>条件为 <code>true</code> 返回表达式1的值，否则返回表达式2的值。</li>
            </ul>
          </td>
          <td>
            <pre class="javascript"><code>
1 ? 'yes' : 'no';               // yes
0 ? 'yes' : 'no';               // no
0 ? 'yes' : 0 ? 'yes' : 'no';   // no 等同于下面的运算
0 ? 'yes' : (0 ? 'yes' : 'no'); // no </code></pre>
          </td>
        </tr>
      </tbody>
    </table>
    <h2>二进制位运算符</h2>
    <p>二进制位运算符有 7 个，用于直接对二进制位进行计算。它直接处理每一个比特位，是非常底层的运算，优点是速度极快，缺点是很不直观，使代码难以理解且容易出错。</p>
    <table class="table-border text-nowrap">
      <thead>
        <tr>
          <th>类型</th>
          <th>符号</th>
          <th>说明</th>
          <th>示例</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>运算规则</th>
          <td></td>
          <td>
            <ol>
              <li>JavaScript 会自动把64位浮点数，转成32位整数，再进行运算。</li>
              <li>大于32位的数位会舍去。</li>
              <li>小数部分会舍去。</li>
              <li>逐位比较每个二进制位。</li>
            </ol>
          </td>
          <td>
            <pre><code class="javascript">0 | 3.1;  // 3 二进制 00 和 11 结果 11 小数部分会舍去</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>二进制且 and</h3>
          </th>
          <td><code>&</code></td>
          <td>逐位比较两个二进制位，存在 <code>0</code> 则结果为 <code>0</code>，否则为 <code>1</code>。</td>
          <td>
            <pre><code class="javascript">0 & 3;    // 0 二进制 00 和 11 结果 00
2 & 3;    // 2 二进制 10 和 11 结果 10
4 & 3;    // 0 二进制 100 和 11 结果 000</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>二进制或 or</h3>
          </th>
          <td><code>|</code></td>
          <td>逐位比较两个二进制位，存在 <code>1</code> 则结果为 <code>1</code>，否则为 <code>0</code>。</td>
          <td>
            <pre><code class="javascript">0 | 3;    // 3 二进制 00 和 11 结果 11
2 | 3;    // 3 二进制 10 和 11 结果 11
4 | 3;    // 7 二进制 100 和 11 结果 111</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>二进制否 not</h3>
          </th>
          <td><code>~</code></td>
          <td>将每个二进制位都变为相反值。自身与取反值相加为 -1。</td>
          <td>
            <pre><code class="javascript">~ null;   // -1
~ -3;     // 2
~~ 3;     // 3 连续取反得到自身值</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>异或 xor</h3>
          </th>
          <td><code>^</code></td>
          <td>逐位比较两个二进制位，不相同则结果为 <code>1</code>，否则为 <code>0</code>。</td>
          <td>
            <pre><code class="javascript">0 ^ 3;    // 3 二进制 00 和 11 结果 11
2 ^ 3;    // 1 二进制 10 和 11 结果 01
4 ^ 3;    // 7 二进制 100 和 11 结果 111</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>左移 left shift</h3>
          </th>
          <td><code><<</code></td>
          <td>将一个数的二进制值向左移动 N 位数，尾部补 <code>0</code>，即 <code>* 2<sup>N</sup></code>。</td>
          <td>
            <pre><code class="javascript">2 << 1;   // 4 二进制 10 结果 100
4 << 1;   // 8 二进制 100 结果 1000
-4 << 1;  // -8</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>右移 right shift</h3>
          </th>
          <td><code>>></code></td>
          <td>将一个数的二进制值向右移动 N 位数，头部补 <code>0</code>，即 <code>/ 2<sup>N</sup></code>。</td>
          <td>
            <pre><code class="javascript">2 >> 1;   // 1 二进制 10 结果 1
4 >> 1;   // 2 二进制 100 结果 10
-4 << 1;  // -2</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>右移（带符号位）<br />zero filled right shift</h3>
          </th>
          <td><code>>>></code></td>
          <td>包括符号位的右移运算符。正数完全一致，负数有区别，运算结果总是为正。</td>
          <td>
            <pre><code class="javascript">4 >>> 1;  // 2
-4 >>> 1; // 2147483646</code></pre>
          </td>
        </tr>
      </tbody>
    </table>
    <h2>其它运算符</h2>
    <table class="table-border text-nowrap">
      <thead>
        <tr>
          <th>类型</th>
          <th>符号</th>
          <th>说明</th>
          <th>示例</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>
            <h3>赋值运算符</h3>
          </th>
          <td><code>x = y</code></td>
          <td>
            <ul>
              <li>将等号右边的值赋给左边。</li>
              <li>等号可以和其它运算符结合，形成变体。</li>
              <li>复合的赋值运算符，先进行指定运算，再将返回值赋给左边的变量。</li>
              <li><b>右结合：</b>多个赋值运算符从右边开始计算。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">var x = 1;
x = 2;  // 2
x += 2; //  3 即 x = x + 2
x -= 2; // -1 即 x = x - 2
x *= 2; //  2 即 x = x * 2
var y = 2;
var z = 3;
x = y = z;  // 3 等同于 x = (y = z)</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>逗号运算符</h3>
          </th>
          <td><code>,</code></td>
          <td>
            <ul>
              <li>执行两个表达式，并返回后一个表达式的值。</li>
              <li>主要用于在返回一个值之前，进行一些辅助操作。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">var x = 0;
var y = (x++, 10);        // 先计算x为1，再计算y为10
var z = (alert(1), true); // 先弹出1，再计算z为true</code></pre>
          </td>
        </tr>
        <tr>
          <th>
            <h3>void 运算符</h3>
          </th>
          <td><code>void</code></td>
          <td>
            <ul>
              <li>执行一个表达式，并返回 <code>undefined</code>。</li>
              <li>优先级很高，表达式应该总是写在圆括号内，否则容易出错。</li>
              <li>主要用于在超链接中插入代码防止网页跳转。</li>
            </ul>
          </td>
          <td>
            <pre><code class="javascript">void(0);     // undefined
void(1 + 2); // undefined
void 1 + 2;  // NaN 等同于 void(1) + 2 即 undefined + 2</code></pre>
            <pre><code class="html">&lt;a href="javascript:void(0)"&gt;防止跳转&lt;/a&gt;</code></pre>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
  <script src="../scripts/article.js"></script>
</body>

</html>
